package com.mvc.framwork.servlet;

import com.mvc.framwork.annotations.SelfAutowire;
import com.mvc.framwork.annotations.SelfController;
import com.mvc.framwork.annotations.SelfRequestMapping;
import com.mvc.framwork.annotations.SelfService;
import com.mvc.framwork.pojo.Handler;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Author: xiaoqiang
 * @Description: 中央控制器
 * @Date: 2021/6/15
 * @Version: 1.0
 */
public class DispatchServlet extends HttpServlet {

    private static final Properties PROPERTIES = new Properties();

    private List<String> list = new ArrayList<>(); //缓存带注解的类全路径

    private Map<String, Object> map = new HashMap<>(); //ioc容器

//    private Map<String, Method> urlMethod = new HashMap<>(); //缓存url 和map的映射

    private List<Handler> urlHandler = new ArrayList<>(); //缓存url 和method的信息

    @Override
    public void init(ServletConfig config) throws ServletException {
        // 1 加载spring mvc配置文件
        String initParam = config.getInitParameter("initParam");
        try {
            doLoadConfig(initParam);
        } catch (IOException e) {
            e.printStackTrace();
        }

        //2 扫描注解相关类
        String aPackage = PROPERTIES.getProperty("package");
        try {
            doScanAnno(aPackage);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        //3 初始化Bean对象
        try {
            doInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }

        //4 实现依赖注入
        doAutowire();

        //5 实现一个HandlerMapping（url 和 method 一一对应）
        doHandlerMapping();

        //6 处理请求
        System.out.println("-------处理请求-------");
    }

    /**
     * 根据url找到类
     */
    private void doHandlerMapping() {
        if (map.size() == 0) {
            return;
        }

        map.forEach((k, v) -> {
            String BashUrl = "";
            boolean b = v.getClass().isAnnotationPresent(SelfController.class);
            if (b) {
                SelfRequestMapping annotation = v.getClass().getAnnotation(SelfRequestMapping.class);
                String value = annotation.value() == null ? "" : annotation.value();
                BashUrl += value;
                Method[] methods = v.getClass().getMethods();
                for (Method method : methods) {
                    if (method.isAnnotationPresent(SelfRequestMapping.class)) {
                        SelfRequestMapping annotation1 = method.getAnnotation(SelfRequestMapping.class);
                        String value1 = annotation1.value();
                        BashUrl += value1;
                        //获取方法参数
                        Parameter[] parameters = method.getParameters();
                        Map<String, Integer> param = new HashMap<>();
                        for (int i = 0; i < parameters.length; i++) {
                            Parameter parameter = parameters[i];
                            //判断参数类型
                            if (parameter.getType() == HttpServletRequest.class || parameter.getType() == HttpServletResponse.class) {
                                param.put(parameter.getType().getSimpleName(), i);
                            } else {
                                param.put(parameter.getName(), i);
                            }

                            param.put(parameter.getName(), i);
                        }
                        Pattern compile = Pattern.compile(BashUrl);
                        Handler handler = new Handler(v, method, compile, param);
                        urlHandler.add(handler);
                    }

                }

            }
        });

    }

    private void doAutowire() {
        try {
            if (map.size() == 0) {
                return;
            }
            map.forEach((k, v) -> {
                //要判断类中的属性上是否有@SelfAutowire注解
                Field[] declaredFields = v.getClass().getDeclaredFields();
                for (Field declaredField : declaredFields) {
                    boolean annotationPresent = declaredField.isAnnotationPresent(SelfAutowire.class);
                    if (annotationPresent) {
                        //如果存在@SelfAutowire,那么给属性赋上值
                        declaredField.setAccessible(true);
                        String name = declaredField.getType().getName(); //UserService  //根据接口找到ioc中的Bean
                        Object o = map.get(name);
                        try {
                            declaredField.set(v, o);
                          map.put(name,o);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }

            });

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private void doInstance() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        if (list.size() == 0) {
            return;
        }
        ;
        for (int i = 0; i < list.size(); i++) {
            String classPath = list.get(i); //com.mvc.framwork.controller.UserController
            Class<?> aClass = Class.forName(classPath);
            String simpleName = aClass.getSimpleName(); //UserController
            String s = convertFirstziMu(simpleName); //userController
            Object o = aClass.newInstance();
            //判断该类上是否有@SelfController
            boolean annotationPresent = aClass.isAnnotationPresent(SelfController.class);
            if (annotationPresent) {
                //放到ioc容器
                map.put(s, o);
            } else if (aClass.isAnnotationPresent(SelfService.class)) {
                //@SelfService的判断是否指定的id名{ @SelfService(value="xxx") }
                SelfService annotation = aClass.getAnnotation(SelfService.class);
                String value = annotation.value();
                if ("".equals(value)) {
                    map.put(s, o);
                } else {
                    map.put(value, o);
                }

                //service 还需要把接口名作为id也存到ioc容器中，便于注入使用
                Class<?>[] interfaces = aClass.getInterfaces();
                for (Class<?> anInterface : interfaces) {
                    map.put(anInterface.getName(), o);
                }

            }
        }

    }

    /**
     * 扫描注解类
     */
    private void doScanAnno(String path) throws ClassNotFoundException {

        //class/com/mvc/framwork
        String basePath = Thread.currentThread().getContextClassLoader().getResource("").getPath() + path.replaceAll("\\.", "/");
        File file = new File(basePath);
        File[] files = file.listFiles();
        if (files != null && files.length > 0) {
            for (File file1 : files) {

                if (file1.isDirectory()) { //子目录
                    String name = file1.getName();
                    String s = path + "/" + name;
                    doScanAnno(s);
                } else if (file1.getName().endsWith(".class")) {
                    String name = file1.getName();
                    String s = path + "/" + name;
                    String pathClass = s.replaceAll("/", ".").replaceAll(".class", "");
                    Class<?> aClass = Class.forName(pathClass);
                    boolean b1 = aClass.isAnnotationPresent(SelfController.class);
                    boolean b2 = aClass.isAnnotationPresent(SelfService.class);
                    if (b1 || b2) {
                        list.add(pathClass);
                    }

                }
            }
        }

    }

    /**
     * 加载springmvc配置文件
     *
     * @param initParam
     * @throws IOException
     */
    private void doLoadConfig(String initParam) throws IOException {
        InputStream in = this.getClass().getClassLoader().getResourceAsStream(initParam);
        PROPERTIES.load(in);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ///处理请求
        try {
            String s = req.getRequestURI().toString();
            //参数
            Map parameterMap = req.getParameterMap();
//        System.out.println("访问uri" + s);
            if (urlHandler != null && urlHandler.size() > 0) {
                for (Handler handler : urlHandler) {
                    Matcher matcher = handler.getPattern().matcher(s);
                    if (matcher.matches()) {            //如果匹配
                        Method method = handler.getMethod();
                        //获取方法参数个数
                        Parameter[] parameters = method.getParameters();
                        Object obj = handler.getController();
                        Map<String, Integer> param = handler.getParam();
                        Object[] objects = new Object[parameters.length];
                        if (parameterMap != null && parameterMap.size() > 0) {
                            parameterMap.forEach((k, v) -> {
                                Integer integer = param.get(k);
                                if (integer != null) {
                                    objects[integer] = v+"";
                                }
                            });
                        }

                        List<Object> objects1 = Arrays.asList(objects);
                        method.invoke(obj, objects);

                    }
                }
            } else {
                resp.getWriter().write("404");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public String convertFirstziMu(String param) {
        String substring = param.substring(0, 1);
        String s = substring.toUpperCase();
        return s + param.substring(1, param.length());
    }
}
